package com.example.minisqlite;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.util.List;

public final class RowCodec {

    private RowCodec() {
    }

    public static byte[] encode(List<ColumnDefinition> columns, Object[] values) throws SQLException {
        if (values.length != columns.size()) {
            throw new SQLException("Value count does not match column count");
        }
        int size = 4; // column count
        for (int i = 0; i < columns.size(); i++) {
            size += 1; // type tag
            Object value = values[i];
            ColumnType type = columns.get(i).getType();
            if (value == null) {
                continue;
            }
            switch (type) {
                case INTEGER:
                    size += 8;
                    break;
                case REAL:
                    size += 8;
                    break;
                case TEXT:
                    byte[] bytes = value.toString().getBytes(StandardCharsets.UTF_8);
                    size += 4 + bytes.length;
                    break;
                default:
                    throw new SQLException("Unsupported column type: " + type);
            }
        }
        ByteBuffer buffer = ByteBuffer.allocate(size);
        buffer.putInt(columns.size());
        for (int i = 0; i < columns.size(); i++) {
            ColumnType type = columns.get(i).getType();
            Object value = values[i];
            if (value == null) {
                buffer.put((byte) 0);
                continue;
            }
            switch (type) {
                case INTEGER:
                    buffer.put((byte) 1);
                    buffer.putLong(((Number) value).longValue());
                    break;
                case REAL:
                    buffer.put((byte) 2);
                    buffer.putDouble(((Number) value).doubleValue());
                    break;
                case TEXT:
                    buffer.put((byte) 3);
                    byte[] bytes = value.toString().getBytes(StandardCharsets.UTF_8);
                    buffer.putInt(bytes.length);
                    buffer.put(bytes);
                    break;
                default:
                    throw new SQLException("Unsupported column type: " + type);
            }
        }
        return buffer.array();
    }

    public static Object[] decode(List<ColumnDefinition> columns, byte[] payload) throws SQLException {
        ByteBuffer buffer = ByteBuffer.wrap(payload);
        int columnCount = buffer.getInt();
        if (columnCount != columns.size()) {
            throw new SQLException("Corrupted row: column count mismatch");
        }
        Object[] values = new Object[columnCount];
        for (int i = 0; i < columnCount; i++) {
            byte tag = buffer.get();
            switch (tag) {
                case 0:
                    values[i] = null;
                    break;
                case 1:
                    values[i] = buffer.getLong();
                    break;
                case 2:
                    values[i] = buffer.getDouble();
                    break;
                case 3:
                    int len = buffer.getInt();
                    byte[] bytes = new byte[len];
                    buffer.get(bytes);
                    values[i] = new String(bytes, StandardCharsets.UTF_8);
                    break;
                default:
                    throw new SQLException("Unknown value tag: " + tag);
            }
        }
        return values;
    }
}
